package IC.SymbolTable;

import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;

import IC.Constants;
import IC.SemanticChecks.SemanticError;
import IC.SemanticChecks.SemanticErrorType;

/**
 * An abstract base class for all symbol tables types
 */
public abstract class SymbolTable {

	public static final int NO_APPERANCE_ORDER = -1;
	private static int uniqueIdCounter = 1;							
	
	private String id;												 //Symbol ID
	private String DisplayId;										 //Symbol Display ID (Used in toString functions)
	private final int uniqueID;										 //Symbol uniqueID
	private LinkedHashMap<String, SymbolTable> childrenSymbolTables; //Childrens
	private SymbolTable parentSymbolTable;							 //Parent
	
	//C'tor
	public SymbolTable(String id,String DisplayId) {
		this.id = id;
		this.DisplayId = DisplayId;
		this.uniqueID = uniqueIdCounter++;
		this.childrenSymbolTables = new LinkedHashMap<String, SymbolTable>();
		this.parentSymbolTable=null;
	}

	public String getId() {
		return id;
	}
	
	public String  getDisplayId() {
		return DisplayId;
	}
	
	public SymbolTable getParent() {
		return parentSymbolTable;
	}

	public SymbolTable getChild(String name) {
		return childrenSymbolTables.get(name);
	}

	public Collection<SymbolTable> getChildren() {
		return childrenSymbolTables.values();
	}

	public void setParent(SymbolTable parent) {
		this.parentSymbolTable = parent;
	}

	public void setChild(SymbolTable child) {
		childrenSymbolTables.put(child.id, child);
	}

	public void insertEntry(Symbol symbol, int lineNumber) throws SemanticError {
		if (getValue(symbol.getId()) != null) 
			throw new SemanticError(SemanticErrorType.INVALID_TYPE_NAME, lineNumber);
		
		symbol.setScopeUniqueID(uniqueID);
		symbol.setSymbolTableRef(this);
		getInnerHashMap(symbol.getKind(), lineNumber).put(symbol.getId(), symbol);
	}

	public abstract Symbol getValue(String id);
	public abstract Symbol lookup(String id, int AppOrder);	
	public abstract Symbol staticLookup(String id);	
	public abstract Symbol virtualLookup(String id);
	
	public Symbol lookup(String id) {
		return lookup(id, NO_APPERANCE_ORDER);
	}
	
	public abstract String Title();
	
	protected abstract HashMap<String, Symbol> getInnerHashMap(SymbolKind kind, int lineNumber) throws SemanticError;

	protected String childrensToString() {
		
		if (childrenSymbolTables.isEmpty())
			return "";
		
		String str = "Children tables: ";
		Boolean flag = true;
		for (SymbolTable symbolTable : childrenSymbolTables.values()) {
			if (flag) 
			{
				str += symbolTable.getDisplayId();
				flag = !flag;
			} 
			else 
				str += ", " + symbolTable.getDisplayId();
		}
		
		return str + Constants.LINE_BREAK;
	}
	
}
